/**
 *    Copyright (C) 2008, 2009  Victor Smirnov <aist11@gmail.com>
 *
 *    This program is free software; you can redistribute it and/or modify
 *    it under the terms of the GNU General Public License as published by
 *    the Free Software Foundation; either version 3 of the License, or
 *    (at your option) any later version.
 *
 *    This program is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *    GNU General Public License for more details.
 *
 *    You should have received a copy of the GNU General Public License along
 *    with this program; if not, write to the Free Software Foundation, Inc.,
 *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 *    See LICENSE.exceptions in the root folder of this project for possible
 *    exceptions
 *
 */

#ifndef _CUBEFS_CORE_MODELS_IDX_MAP_MODEL_TOOLS_HPP
#define	_CUBEFS_CORE_MODELS_IDX_MAP_MODEL_TOOLS_HPP

#include <cubefs/core/container/container.hpp>
#include <cubefs/core/btree/pages/tools.hpp>
#include <cubefs/core/models/idx_map/names.hpp>
#include <cubefs/api/models/logs.hpp>

#define CUBEFS_SOURCE "cubefs/core/models/idx_map/model/tools.hpp:" CUBEFS_AT

namespace cubefs    {
namespace ctr       {

using namespace cubefs::btree;

CUBEFS_MODEL_PART_BEGIN(cubefs::models::idx_map::ToolsName)

    typedef typename Base::Types                                                Types;
    typedef typename Base::PageMgr                                              PageMgr;

    typedef typename PageMgr::Page                                              Page;
    typedef typename Page::ID                                                   ID;
    typedef typename PageMgr::Transaction                                       Transaction;

    typedef typename Types::NodeBase                                            NodeBase;
    typedef typename Types::Counters                                            Counters;
    typedef typename Base::Iterator                                             Iterator;

    typedef typename Types::Pages::NodeDispatcher                               NodeDispatcher;
    typedef typename Types::Pages::RootDispatcher                               RootDispatcher;
    typedef typename Types::Pages::LeafDispatcher                               LeafDispatcher;
    typedef typename Types::Pages::NonLeafDispatcher                            NonLeafDispatcher;

    typedef typename Types::Pages::Node2RootMap                                 Node2RootMap;
    typedef typename Types::Pages::Root2NodeMap                                 Root2NodeMap;

    typedef typename Types::Pages::NodeFactory                                  NodeFactory;

    typedef typename Base::Key                                                  Key;
    typedef typename Base::Value                                                Value;

    static const Int Indexes                                                    = Types::Indexes;
    static const Int MapType                                                    = Types::MapType;
    
    struct SetAndReindexFn1 {
        Int             i_;
        const Key*      keys_;
        const Value&    data_;
        bool            fixed_;
    public:
        SetAndReindexFn1(Int i, const Key *keys, const Value& data): i_(i), keys_(keys), data_(data), fixed_(true) {}

        template <typename T>
        void operator()(T *node)
        {
            for (Int c = 0; c < Indexes; c++) {
                node->map().key(c, i_) = keys_[c];
            }

            node->map().data(i_) = data_;

            if (i_ < node->map().size() - 1) {
                for (Int c = 0; c < Indexes; c++) {
                    node->map().key(c, i_ + 1) -= keys_[c];
                }
            }
            else {
                fixed_ = false;
            }

            node->map().Reindex();
        }
    };


    struct SetAndReindexFn2 {
        Int             i_;
        const Key*      keys_;

    public:
        SetAndReindexFn2(Int i, const Key *keys): i_(i), keys_(keys) {}

        template <typename T>
        void operator()(T *node)
        {
            for (Int c = 0; c < Indexes; c++) {
                node->map().key(c, i_) -= keys_[c];
            }

            node->map().Reindex();
        }
    };

    void SetLeafDataAndReindex(NodeBase *node, Int idx, const Key *keys, const Value &val) {
        CUBEFS_TRACE(me_, "SetLeafDataAndReindex", node->id(), idx, keys[0]);
        SetAndReindexFn1 fn1(idx, keys, val);
        LeafDispatcher::Dispatch(node, fn1);

        if (!fn1.fixed_) {
            Iterator i(node, idx, me_);
            if (i.NextKey())
            {
                CUBEFS_TRACE(me_, "Try to fix next key", i.page()->id(), idx, -keys[0]);
                SetAndReindexFn2 fn2(i.key_idx(), keys);
                LeafDispatcher::Dispatch(i.page(), fn2);

                if (MapType == MapTypes::Index)
                {
                    me_.UpdateBTreeKeys(i.page());
                }
                else if (i.key_idx() >= me_.GetChildrenCount(i.page()) -1) {
                    me_.UpdateBTreeKeys(i.page());
                }
            }
        }
    }

    template <typename Node>
    bool CheckNodeContent(Node *node) {
        bool errors = false;

        for (Int i = 0; i < Indexes; i++) {
            Key key = 0;

            for (Int c = 0; c < node->map().size(); c++) {
                key += node->map().key(i, c);
            }

            if (key != node->map().max_key(i)) {
                CUBEFS_ERROR(me_, "Sum of keys doen't match max_key for key", i, key, node->map().max_key(i));
                errors = true;
            }
        }
        
        return errors;
    }

    template <typename Node1, typename Node2>
    bool CheckNodeWithParentContent(Node1 *node, Node2 *parent) {
        bool errors = false;
        for (Int c = 0; c < Indexes; c++) {
            if (node->map().max_key(c) != parent->map().key(c, node->parent_idx())) {
                CUBEFS_ERROR(me_, "Invalid parent-child nodes chain", c, node->map().max_key(c), parent->map().key(c, node->parent_idx()), "for", node->id(), parent->id(), node->parent_idx());
                errors = true;
            }
        }

        errors = CheckNodeContent(node) || errors;

        return errors;
    }
    
    Iterator GetByIndex(BigInt index) {
        Iterator i = Base::GetByIndex(index);
        i.ComputeBase();
        return i;
    }

CUBEFS_MODEL_PART_END

}
}

#undef CUBEFS_SOURCE
#endif
